/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.commons.imaging;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;

/**
 * ImageInfo represents a collection of basic properties of an image, such as
 * width, height, format, bit depth, etc.
 */
public class ImageInfo {
    private final String formatDetails; // ie version

    private final int bitsPerPixel;
    private final List<String> comments;

    private final ImageFormat format;
    private final String formatName;
    private final int height;
    private final String mimeType;

    private final int numberOfImages;
    private final int physicalHeightDpi;
    private final float physicalHeightInch;
    private final int physicalWidthDpi;
    private final float physicalWidthInch;
    private final int width;
    private final boolean isProgressive;
    private final boolean isTransparent;

    private final boolean usesPalette;

    public static final int COLOR_TYPE_BW = 0;
    public static final int COLOR_TYPE_GRAYSCALE = 1;
    public static final int COLOR_TYPE_RGB = 2;
    public static final int COLOR_TYPE_CMYK = 3;
    public static final int COLOR_TYPE_YCbCr = 4;
    public static final int COLOR_TYPE_YCCK = 5;
    public static final int COLOR_TYPE_YCC = 6;
    public static final int COLOR_TYPE_OTHER = -1;
    public static final int COLOR_TYPE_UNKNOWN = -2;

    private final int colorType;

    public static final String COMPRESSION_ALGORITHM_UNKNOWN = "Unknown";
    public static final String COMPRESSION_ALGORITHM_NONE = "None";
    public static final String COMPRESSION_ALGORITHM_LZW = "LZW";
    public static final String COMPRESSION_ALGORITHM_PACKBITS = "PackBits";
    public static final String COMPRESSION_ALGORITHM_JPEG = "JPEG";
    public static final String COMPRESSION_ALGORITHM_RLE = "RLE: Run-Length Encoding";
    public static final String COMPRESSION_ALGORITHM_PSD = "Photoshop";
    public static final String COMPRESSION_ALGORITHM_PNG_FILTER = "PNG Filter";
    public static final String COMPRESSION_ALGORITHM_CCITT_GROUP_3 = "CCITT Group 3 1-Dimensional Modified Huffman run-length encoding.";
    public static final String COMPRESSION_ALGORITHM_CCITT_GROUP_4 = "CCITT Group 4";
    public static final String COMPRESSION_ALGORITHM_CCITT_1D = "CCITT 1D";

    private final String compressionAlgorithm;

    public ImageInfo(final String formatDetails, final int bitsPerPixel,
            final List<String> comments, final ImageFormat format, final String formatName,
            final int height, final String mimeType, final int numberOfImages,
            final int physicalHeightDpi, final float physicalHeightInch,
            final int physicalWidthDpi, final float physicalWidthInch, final int width,
            final boolean isProgressive, final boolean isTransparent, final boolean usesPalette,
            final int colorType, final String compressionAlgorithm) {
        this.formatDetails = formatDetails;

        this.bitsPerPixel = bitsPerPixel;
        this.comments = comments;

        this.format = format;
        this.formatName = formatName;
        this.height = height;
        this.mimeType = mimeType;

        this.numberOfImages = numberOfImages;
        this.physicalHeightDpi = physicalHeightDpi;
        this.physicalHeightInch = physicalHeightInch;
        this.physicalWidthDpi = physicalWidthDpi;
        this.physicalWidthInch = physicalWidthInch;
        this.width = width;
        this.isProgressive = isProgressive;

        this.isTransparent = isTransparent;
        this.usesPalette = usesPalette;

        this.colorType = colorType;
        this.compressionAlgorithm = compressionAlgorithm;
    }

    /**
     * Returns the bits per pixel of the image data.
     */
    public int getBitsPerPixel() {
        return bitsPerPixel;
    }

    /**
     * Returns a list of comments from the image file.
     * <p/>
     * This is mostly obsolete.
     */
    public List<String> getComments() {
        return new ArrayList<String>(comments);
    }

    /**
     * Returns the image file format, ie. ImageFormat.IMAGE_FORMAT_PNG.
     * <p/>
     * Returns ImageFormat.IMAGE_FORMAT_UNKNOWN if format is unknown.
     * 
     * @return A constant defined in ImageFormat.
     * @see ImageFormat
     */
    public ImageFormat getFormat() {
        return format;
    }

    /**
     * Returns a string with the name of the image file format.
     * 
     * @see #getFormat()
     */
    public String getFormatName() {
        return formatName;
    }

    /**
     * Returns the height of the image in pixels.
     * 
     * @see #getWidth()
     */
    public int getHeight() {
        return height;
    }

    /**
     * Returns the MIME type of the image.
     * 
     * @see #getFormat()
     */
    public String getMimeType() {
        return mimeType;
    }

    /**
     * Returns the number of images in the file.
     * <p>
     * Applies mostly to GIF and TIFF; reading PSD/Photoshop layers is not
     * supported, and Jpeg/JFIF EXIF thumbnails are not included in this count.
     */
    public int getNumberOfImages() {
        return numberOfImages;
    }

    /**
     * Returns horizontal dpi of the image, if available.
     * <p>
     * Applies to TIFF (optional), BMP (always), GIF (constant: 72), Jpeg
     * (optional), PNG (optional), PNM (constant: 72), PSD/Photoshop (constant:
     * 72).
     * 
     * @return returns -1 if not present.
     */
    public int getPhysicalHeightDpi() {
        return physicalHeightDpi;
    }

    /**
     * Returns physical height of the image in inches, if available.
     * <p>
     * Applies to TIFF (optional), BMP (always), GIF (constant: 72), Jpeg
     * (optional), PNG (optional), PNM (constant: 72), PSD/Photoshop (constant:
     * 72).
     * 
     * @return returns -1 if not present.
     */
    public float getPhysicalHeightInch() {
        return physicalHeightInch;
    }

    /**
     * Returns vertical dpi of the image, if available.
     * <p>
     * Applies to TIFF (optional), BMP (always), GIF (constant: 72), Jpeg
     * (optional), PNG (optional), PNM (constant: 72), PSD/Photoshop (constant:
     * 72).
     * 
     * @return returns -1 if not present.
     */
    public int getPhysicalWidthDpi() {
        return physicalWidthDpi;
    }

    /**
     * Returns physical width of the image in inches, if available.
     * <p>
     * Applies to TIFF (optional), BMP (always), GIF (constant: 72), Jpeg
     * (optional), PNG (optional), PNM (constant: 72), PSD/Photoshop (constant:
     * 72).
     * 
     * @return returns -1 if not present.
     */
    public float getPhysicalWidthInch() {
        return physicalWidthInch;
    }

    /**
     * Returns the width of the image in pixels.
     * 
     * @see #getHeight()
     */
    public int getWidth() {
        return width;
    }

    /**
     * Returns true if the image is progressive or interlaced.
     */
    public boolean isProgressive() {
        return isProgressive;
    }

    /**
     * Returns the color type of the image, as a constant (ie.
     * ImageFormat.COLOR_TYPE_CMYK).
     * 
     * @see #getColorTypeDescription()
     */
    public int getColorType() {
        return colorType;
    }

    /**
     * Returns a description of the color type of the image.
     * 
     * @see #getColorType()
     */
    public String getColorTypeDescription() {
        switch (colorType) {
        case COLOR_TYPE_BW:
            return "Black and White";
        case COLOR_TYPE_GRAYSCALE:
            return "Grayscale";
        case COLOR_TYPE_RGB:
            return "RGB";
        case COLOR_TYPE_CMYK:
            return "CMYK";
        case COLOR_TYPE_YCbCr:
            return "YCbCr";
        case COLOR_TYPE_YCCK:
            return "YCCK";
        case COLOR_TYPE_YCC:
            return "YCC";
        case COLOR_TYPE_OTHER:
            return "Other";
        case COLOR_TYPE_UNKNOWN:
            return "Unknown";

        default:
            return "Unknown";
        }

    }

    public void dump() {
        System.out.print(toString());
    }

    @Override
    public String toString() {
        try {
            final StringWriter sw = new StringWriter();
            final PrintWriter pw = new PrintWriter(sw);

            toString(pw, "");
            pw.flush();

            return sw.toString();
        } catch (final Exception e) {
            return "Image Data: Error";
        }
    }

    public void toString(final PrintWriter pw, final String prefix) {
        pw.println("Format Details: " + formatDetails);

        pw.println("Bits Per Pixel: " + bitsPerPixel);
        pw.println("Comments: " + comments.size());
        for (int i = 0; i < comments.size(); i++) {
            final String s = comments.get(i);
            pw.println("\t" + i + ": '" + s + "'");

        }
        pw.println("Format: " + format.getName());
        pw.println("Format Name: " + formatName);
        pw.println("Compression Algorithm: " + compressionAlgorithm);
        pw.println("Height: " + height);
        pw.println("MimeType: " + mimeType);
        pw.println("Number Of Images: " + numberOfImages);
        pw.println("Physical Height Dpi: " + physicalHeightDpi);
        pw.println("Physical Height Inch: " + physicalHeightInch);
        pw.println("Physical Width Dpi: " + physicalWidthDpi);
        pw.println("Physical Width Inch: " + physicalWidthInch);
        pw.println("Width: " + width);
        pw.println("Is Progressive: " + isProgressive);
        pw.println("Is Transparent: " + isTransparent);

        pw.println("Color Type: " + getColorTypeDescription());
        pw.println("Uses Palette: " + usesPalette);

        pw.flush();

    }

    /**
     * Returns a description of the file format, ie. format version.
     */
    public String getFormatDetails() {
        return formatDetails;
    }

    /**
     * Returns true if the image has transparency.
     */
    public boolean isTransparent() {
        return isTransparent;
    }

    /**
     * Returns true if the image uses a palette.
     */
    public boolean usesPalette() {
        return usesPalette;
    }

    /**
     * Returns a description of the compression algorithm, if any.
     */
    public String getCompressionAlgorithm() {
        return compressionAlgorithm;
    }

}
